[AWS] SESとLambda関数でメールの一斉送信を実装してみた
Amazon Simple Email Service (Amazon SES) は、Amazonが提供するフルマネージド型のEメール配信サービスです。初期費用不要&従量課金制の料金体系で、基本的にはメールの送信数でコストが決まります。また、高可用性およびスケーラビリティなサービスなので、大量のメールを配信するケースにも対応できます。
本記事ではSESを利用して会員サイトのユーザーにメールを一斉送信する機能を実装してみます。
SESで送信元メールアドレスを登録
送信元のメールアドレスをSESに登録します。サンドボックス環境の場合は送信先のメールアドレスも登録しておきます。承認されていないメールアドレスに対してメールを送信する場合、サンドボックス環境の解除を申請する必要があります。
DynamoDBでユーザーテーブルを作成
メール送信先のユーザーリストを管理するテーブルを作成します。
ユーザーテーブルに送信先のユーザーを登録します。
userId(PK) | name | emailAddress |
---|---|---|
a06b7c45-1303-45bf-a2d7-fec53fbc224c | 田中太郎 | takahashi.yudai+test1@*****.jp |
1d484776-e2ee-40b6-a167-1f82603a14d8 | 鈴木一郎 | takahashi.yudai+test1@*****.jp |
cf57a613-c01c-4bd2-bf78-fe1f7a1ba920 | 山田花子 | takahashi.yudai+test1@*****.jp |
メール配信のLambda関数を実装
メール配信用のLambda関数を実装します。送信先のリストが多い場合、Lambdaのメモリとタイムアウトの設定に注意してください。また、Lambda関数を実装する際、DynamoDBとSESの権限が必要になります。
const DynamoDB = require('aws-sdk/clients/dynamodb'); const SES = require('aws-sdk/clients/ses'); const USER_TABLE_NAME = 'User'; const dynamodbDocumentClient = new DynamoDB.DocumentClient({ apiVersion: '2012-10-08', region: 'ap-northeast-1', }); const sesClient = new SES({ signatureVersion: 'v4', region: 'us-east-1', }); const templateName = 'MyTemplate'; // メールのテンプレート名 const source = '[email protected]'; // 送信元メールアドレス const emailSubject = 'テストメール'; // メール件名 const emailText = '{{name}}様\r\n\r\nこれはテストメールです。'; // メール本文 /** * ユーザーリストを取得 */ const getUserItemList = async () => { const params = { TableName: USER_TABLE_NAME, }; let userItemList = []; do { const response = await dynamodbDocumentClient.scan(params).promise(); params.ExclusiveStartKey = response.LastEvaluatedKey; userItemList = userItemList.concat(response.Items); } while (params.ExclusiveStartKey); return userItemList; }; /** * 配列を指定した個数で分割 * @param array array * @param number size */ const arrayChunk = (array, size) => { const chunks = []; while (0 < array.length) { chunks.push(array.splice(0, size)); } return chunks; }; exports.handler = async () => { const userItemList = await getUserItemList(); const destinations = userItemList.map(userItem => { return { Destination: { ToAddresses: [userItem.emailAddress], }, ReplacementTemplateData: JSON.stringify({ name: userItem.name }), }; }); const destinationChunks = arrayChunk(destinations, 50); await sesClient .createTemplate({ Template: { TemplateName: templateName, SubjectPart: emailSubject, TextPart: emailText, }, }) .promise(); for (let i = 0; i < destinationChunks.length; i++) { await sesClient .sendBulkTemplatedEmail({ Source: source, Template: templateName, Destinations: destinationChunks[i], DefaultTemplateData: JSON.stringify({ name: '名無し', }), }) .promise(); } await sesClient .deleteTemplate({ TemplateName: templateName, }) .promise(); };
- DynamoDBのユーザーテーブルからユーザーリストを取得
- ユーザーリストで宛先リストを作成
- 宛先リストを50個ずつ分割(最大50の宛先までリクエスト可能なので)
- メールのテンプレートを登録
- メールのテンプレートと宛先リストでメールを配信
- メールのテンプレートを削除
SESのレートリミットの都合上、メール送信の部分は意図的に並列処理を避けています。ReplacementTemplateData
で件名や本文のタグを置換できます。
動作確認
Lambda関数を実行してメールが送信されることを確認します。
まとめ
SendBulkTemplatedEmail
オペレーションを利用して簡単にメールの一斉送信を実装することができました。メールの件名や本文にタグを埋め込める機能はとても便利だと思います。
メールの送信先があまりにも多い場合には、Lambdaの実行時間制限である15分を超過してしまうかもしれません。また、メールの配信スピードを向上させる場合など、複数のLambdaで並行してメール送信を実行するケースも考えられます。その際にはSESのレートリミットを考慮して設計する必要があると思いました。